﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Msdn5.Framework
{
    /// <summary>
    /// 新的表达式类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CustomExpression<T>
    {
        /// <summary>
        /// 默认的表达式列表都填到这里
        /// 最后用and 或者 or进行所有表达式的连接
        /// 对每个Item调用GetExpWhere拼接出最终的查询语句
        /// </summary>
        public List<Expression<Func<T, bool>>> DefaultExpressions { get; set; } = new List<Expression<Func<T, bool>>>();

        public Expression<Func<T, bool>> GetDefaultExpressionsWhere(bool UseAnd = true)
        {
            Expression where = null;
            for (int i = 0; i < DefaultExpressions.Count; i++)
            {
                if (where == null)
                {
                    where = DefaultExpressions[i].Body;
                }
                else
                {

                    where = UseAnd ? GetAndExps(where, DefaultExpressions[i].Body) : GetOrExps(where, DefaultExpressions[i].Body);
                }
            }
            return GetExpWhere(where);
        }

        public Expression<Func<T, bool>> GetExpWhere(Expression exp)
        {
            return Expression.Lambda<Func<T, bool>>(exp, Expression.Parameter(typeof(T), "x"));
        }
        /// <summary>
        /// 根据Key创建一个表达式
        /// </summary> 
        /// <param name="key">属性/字段</param>
        /// <param name="val">值</param>
        /// <param name="expFunc">需要拼装的表达式值,例如大于,小于,包含等</param>
        /// <param name="errorAction">异常</param>
        /// <returns>构造的表达式(失败时返回Null)</returns>
        public Expression GetExpForKey(string key, object val, Func<Expression, Expression, Expression> expFunc, Action<Exception> errorAction = null)
        {
            try
            {
                var memberAccess = Expression.PropertyOrField(Expression.Parameter(typeof(T), "x"), key);
                var right = ConvertToExp(memberAccess.Type, val);
                return expFunc.Invoke(memberAccess, right);
            }
            catch (Exception ex)
            {
                if (errorAction != null)
                {
                    errorAction.Invoke(ex);
                }
                else
                {
                    throw ex;
                }
            }
            return null;
        } 
        /// <summary>
        /// 使用表达式访问,废弃反射
        /// </summary>
        /// <param name="key">反射的属性Key</param>
        /// <param name="val">属性值</param>
        /// <returns></returns>
        public Tuple<MemberExpression, ConstantExpression> GetPropertyExpression(string key, object val, Action<Exception> ErrorAction = null)
        {
            Tuple<MemberExpression, ConstantExpression> tuple = null;
            try
            {
                var param = Expression.Parameter(typeof(T), "x");//参数表达式 构造出x=>
                var memberAccess = Expression.PropertyOrField(param, key);
                if (memberAccess != null)
                {
                    //属性表达式构造出 x.key 与 val 但是不包含条件    debugview中为$x.属性
                    var left = memberAccess;
                    var right = ConvertToExp(memberAccess.Type, val);
                    tuple = new Tuple<MemberExpression, ConstantExpression>(left, right);
                }
            }
            catch (Exception ex)
            {
                if (ErrorAction != null)
                {
                    ErrorAction.Invoke(ex);
                }
                else
                {
                    throw ex;
                }
            }

            return tuple;
        }


        /// <summary>
        /// 调用 Convert.ChangeType 将object类型转换为left属性需要的类型
        /// </summary>
        /// <param name="obj">需要转换的参数</param>
        /// <returns>转换后的结果</returns>
        public ConstantExpression ConvertToExp(System.Type type, object obj)
        {

            if (type != obj.GetType())
            {
                //转换为对应类型 
                return Expression.Constant(Convert.ChangeType(obj, type));
            }
            else
            {
                return Expression.Constant(obj, type);
            }
        }
        public Expression GetWhereExp(DataApiParam apiParam)
        {
            //获取DataApiParam的字段 
            Expression where;
            List<Expression> exps = new List<Expression>();
            Tuple<MemberExpression, ConstantExpression> tuple = GetPropertyExpression("Id", 0);
            //拼装默认条件Id>0 
            where = GetGreaterExp(tuple.Item1, tuple.Item2);

            if (!string.IsNullOrEmpty(apiParam.SearchKey) && !string.IsNullOrEmpty(apiParam.SearchVal))
            {
                tuple = GetPropertyExpression(apiParam.SearchKey, apiParam.SearchVal, err => { });
                if (tuple != null)
                {
                    exps.Add(GetContainsExp(tuple.Item1, tuple.Item2));
                }
            }
            if (!string.IsNullOrEmpty(apiParam.SearchSelKey) && !string.IsNullOrEmpty(apiParam.SearchSelVal))
            {
                tuple = GetPropertyExpression(apiParam.SearchSelKey, apiParam.SearchSelVal);
                if (tuple != null)
                {
                    exps.Add(GetEqualExp(tuple.Item1, tuple.Item2));
                }
            }
            if (!string.IsNullOrEmpty(apiParam.SearchDateSelKey) && !string.IsNullOrEmpty(apiParam.SearchStartTimeVal) && !string.IsNullOrEmpty(apiParam.SearchEndTimeVal))
            {
                var st = apiParam.SearchStartTimeVal.ToDateTime();
                var et = apiParam.SearchEndTimeVal.ToDateTime();
                if (st != default(DateTime) && et != default(DateTime))
                {
                    exps.Add(GetTimeExp(apiParam.SearchDateSelKey, st, et));
                }

            }
            if (!string.IsNullOrEmpty(apiParam.SearchNumKey) && !string.IsNullOrEmpty(apiParam.SearchNumStartVal) && !string.IsNullOrEmpty(apiParam.SearchNumEndVal))
            {
                if (decimal.TryParse(apiParam.SearchNumStartVal, out decimal StartVal) && decimal.TryParse(apiParam.SearchNumEndVal, out decimal EndVal))
                {
                    exps.Add(GetNumExp(apiParam.SearchNumKey, StartVal, EndVal));
                }
            }
            for (int i = 0; i < exps.Count; i++)
            {
                where = GetAndExps(where, exps[i]);
            }
            return where;
        }
        /// <summary>
        /// 获取c=>c.actionTime >= st && c.actionTime <= et
        /// </summary>
        /// <returns>表达式</returns>
        public Expression GetTimeExp(string key, DateTime st, DateTime et)
        {
            //c=>c.actionTime >= st && c.actionTime <= et
            Tuple<MemberExpression, ConstantExpression> lTime = GetPropertyExpression(key, st);
            Tuple<MemberExpression, ConstantExpression> rTime = GetPropertyExpression(key, et);
            return GetAndExps(GetGEqualExp(lTime.Item1, lTime.Item2), GetLEqualExp(rTime.Item1, rTime.Item2));
        }

        /// <summary>
        /// 获取c=>c.key >= StartNum && c.key <= EndNum
        /// </summary>
        /// <returns>表达式</returns>
        public Expression GetNumExp(string key, decimal StartNum, decimal EndNum)
        {
            //c=>c.key >= StartNum && c.key <= EndNum
            Tuple<MemberExpression, ConstantExpression> lTime = GetPropertyExpression(key, StartNum);
            Tuple<MemberExpression, ConstantExpression> rTime = GetPropertyExpression(key, EndNum);
            return GetAndExps(GetGEqualExp(lTime.Item1, lTime.Item2), GetLEqualExp(rTime.Item1, rTime.Item2));
        }
        #region 表达式拼接
        /// <summary>
        /// and条件拼接两个表达式
        /// c=>c.left ? right and c.left ? right 表达式
        /// </summary>
        /// <returns>表达式</returns>
        public BinaryExpression GetAndExps(Expression lexp, Expression rexp)
        {

            //c=>c.left ? right and c.left ? right
            return Expression.AndAlso(lexp, rexp);

        }
        /// <summary>
        /// or 条件拼接两个表达式
        /// c=>c.left ? right or c.left ? right 表达式
        /// </summary>
        /// <returns>表达式</returns>
        public BinaryExpression GetOrExps(Expression lexp, Expression rexp)
        {
            //c=>c.left ? right || c.left ? right
            return Expression.OrElse(lexp, rexp);

        }

        #endregion

        #region 常规运算表达式 大于、小于、等于、大于等于、小于等于
        /// <summary>
        /// 获取left == right 表达式
        /// </summary>
        /// <param name="lexp"></param>
        /// <param name="rexp"></param>
        /// <returns></returns>
        public Expression GetEqualExp(Expression lexp, Expression rexp)
        {
            //c=>c.left == right 
            return Expression.Equal(lexp, rexp);
        }
        /// <summary>
        /// 获取left Contains right 表达式
        /// </summary>
        /// <param name="lexp"></param>
        /// <param name="rexp"></param>
        /// <returns></returns>
        public Expression GetContainsExp(Expression lexp, Expression rexp)
        {

            //x=>x.left Contains right ,netstandard 下需要指定参数,否则会抛一个多个参数的异常
            return Expression.Call(lexp, typeof(string).GetMethod("Contains", new[] { typeof(string) }), rexp);
        }

        /// <summary>
        /// 获取left != right 表达式
        /// </summary>
        /// <param name="lexp"></param>
        /// <param name="rexp"></param>
        /// <returns></returns>
        public Expression GetNotEqualExp(Expression lexp, Expression rexp)
        {
            //c=>c.left != right 
            return Expression.NotEqual(lexp, rexp);
        }
        /// <summary>
        /// 获取left <= right 表达式
        /// </summary>
        /// <param name="lexp"></param>
        /// <param name="rexp"></param>
        /// <returns></returns>
        public Expression GetLEqualExp(Expression lexp, Expression rexp)
        {
            //c=>c.left <= right 
            return Expression.LessThanOrEqual(lexp, rexp);
        }
        /// <summary>
        /// 获取left >= right 表达式
        /// </summary>
        /// <param name="lexp"></param>
        /// <param name="rexp"></param>
        /// <returns></returns>
        public Expression GetGEqualExp(Expression lexp, Expression rexp)
        {
            //c=>c.left <= right 
            return Expression.GreaterThanOrEqual(lexp, rexp);
        }
        /// <summary>
        /// 获取left < right 表达式
        /// </summary>
        /// <param name="lexp"></param>
        /// <param name="rexp"></param>
        /// <returns></returns>
        public Expression GetLessExp(Expression lexp, Expression rexp)
        {
            //c=>c.left < right 
            return Expression.LessThan(lexp, rexp);
        }
        /// <summary>
        /// 获取left > right 表达式
        /// </summary>
        /// <param name="lexp"></param>
        /// <param name="rexp"></param>
        /// <returns></returns>
        public Expression GetGreaterExp(Expression lexp, Expression rexp)
        {
            //c=>c.left > right 
            return Expression.GreaterThan(lexp, rexp);
        }
        #endregion

    }
}
